package com.youlu.campus.config;

import com.mongodb.ConnectionString;
import com.mongodb.MongoClientSettings;
import com.mongodb.ReadPreference;
import com.mongodb.client.MongoClient;
import com.mongodb.client.MongoClients;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.mongo.MongoProperties;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.FilterType;
import org.springframework.context.annotation.Primary;
import org.springframework.data.mongodb.MongoDatabaseFactory;
import org.springframework.data.mongodb.SpringDataMongoDB;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.SimpleMongoClientDatabaseFactory;
import org.springframework.data.mongodb.core.convert.DbRefResolver;
import org.springframework.data.mongodb.core.convert.DefaultDbRefResolver;
import org.springframework.data.mongodb.core.convert.DefaultMongoTypeMapper;
import org.springframework.data.mongodb.core.convert.MappingMongoConverter;
import org.springframework.data.mongodb.core.convert.MongoCustomConversions;
import org.springframework.data.mongodb.core.mapping.MongoMappingContext;
import org.springframework.data.mongodb.repository.config.EnableMongoRepositories;

import java.util.concurrent.TimeUnit;

@Configuration
@RequiredArgsConstructor
@EnableConfigurationProperties(MongoProperties.class)
@EnableMongoRepositories(
        basePackages = "com.youlu.campus",
        excludeFilters = {
                //排除官网仓库的 MongoRepository
                @ComponentScan.Filter(type = FilterType.REGEX, pattern = "com\\.youlu\\.campus\\.website\\..*"),  // 排除指定子包
                @ComponentScan.Filter(type = FilterType.REGEX, pattern = "com\\.youlu\\.campus\\.monitor\\..*")  // 排除指定子包
        }
)
@Slf4j
public class AdminMongoTemplateConfig {

    private final MongoProperties mongoProperties;

    @Primary
    @Bean(name = "mongoTemplate")
    public MongoTemplate mongoTemplate(MongoDatabaseFactory mongoDbFactory, MappingMongoConverter mappingMongoConverte) {
        MongoTemplate mongoTemplate = new MongoTemplate(mongoDbFactory, mappingMongoConverte);
        mongoTemplate.setReadPreference(ReadPreference.secondaryPreferred());
        return mongoTemplate;
    }

    @Bean(name = "masterMongoTemplate")
    public MongoTemplate masterMongoTemplate(MongoDatabaseFactory mongoDbFactory, MappingMongoConverter mappingMongoConverte) {
        MongoTemplate mongoTemplate = new MongoTemplate(mongoDbFactory, mappingMongoConverte);
        mongoTemplate.setReadPreference(ReadPreference.primaryPreferred());
        return mongoTemplate;
    }

//    @Bean
//    public MongoClient mongoClient() {
//        log.info(":>>> uri:{}",mongoProperties.getUri());
//        return MongoClients.create(mongoProperties.getUri());
//    }
//    @Bean
//    public SimpleMongoClientDatabaseFactory mongoDbFactory(MongoClient mongoClient) {
//        return new SimpleMongoClientDatabaseFactory(mongoClient,mongoProperties.getDatabase());
//    }
//    @Bean
//    public MongoDbFactory mongoDbFactory(MongoClient mongoClient) {
//        return new SimpleMongoClientDbFactory(mongoClient,mongoProperties.getDatabase());
//    }

    @Bean
    @ConditionalOnMissingBean(MappingMongoConverter.class)
    public MappingMongoConverter mappingMongoConverter(MongoDatabaseFactory mongoDbFactory, MongoMappingContext context, BeanFactory beanFactory) {
        DbRefResolver dbRefResolver = new DefaultDbRefResolver(mongoDbFactory);
        MappingMongoConverter mappingConverter = new MappingMongoConverter(dbRefResolver, context);
        // remove _class field
        mappingConverter.setTypeMapper(new DefaultMongoTypeMapper(null));
        try {
            mappingConverter.setCustomConversions(beanFactory.getBean(MongoCustomConversions.class));
        } catch (NoSuchBeanDefinitionException ex) {
            // Ignore
            log.error("mappingMongoConverter init error", ex);
        }
        return mappingConverter;
    }

    /**
     * 连接池工厂
     *
     * @param properties
     * @return
     */
    @Primary
    @Bean
    public SimpleMongoClientDatabaseFactory mongoDbFactory(MongoProperties properties) {
        MongoClientSettings.Builder builder = MongoClientSettings.builder();
        builder.applyConnectionString(new ConnectionString(properties.getUri()));
        builder.applyToConnectionPoolSettings(b -> {
            b.maxSize(1000);
            b.minSize(10);
            b.maxConnectionLifeTime(0, TimeUnit.SECONDS);
            b.maxConnectionIdleTime(5, TimeUnit.MINUTES);
            b.maxWaitTime(120000, TimeUnit.MILLISECONDS);
        });
        MongoClient mongoClient = MongoClients.create(builder.build(), SpringDataMongoDB.driverInformation());
        return new SimpleMongoClientDatabaseFactory(mongoClient, properties.getDatabase());
    }
}
